﻿using Microsoft.Xna.Framework.Media;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using TakePhotoTogether.ImageProcessing;
using TakePhotoTogether.Model;
using Windows.Phone.Media.Capture;

namespace TakePhotoTogether.Controller
{
    class CameraController
    {
        private const string DEBUG_TAG = "TakePhotoTogether.Controller.CameraController::";

        private bool peerFinderCompleted;
        private CameraCaptureSequence photoSequence;

        public CameraSensorLocation sensor = CameraSensorLocation.Back;

        private readonly MediaLibrary library = new MediaLibrary();
        private PhotoCaptureDevice _photoCaptureDevice = null;
        private readonly Semaphore _cameraSemaphore = new Semaphore(1, 1);

        /// <summary> Final result of photo taken on Split mode</summary>
        private readonly CVImage finalPhotoSplitMode = new CVImage(800, 480);
        /// <summary> Store partner photo taken on Split mode </summary>
        private readonly CVImage partnerPhotoSplitMode = new CVImage(398, 480);
        /// <summary> Store photos taken on Split mode </summary>
        private readonly CVImage cameraPhotoSplitMode = new CVImage(640, 480);

        /// <summary> Final result of photo taken on Full mode</summary>
        private CVImage cameraPhotoFullMode;
        private CVImage croppedPhotoFullMode;
        private readonly CVImage resizedPhotoFullMode = new CVImage(800, 480);

        private bool _isInitializingCamera = false;

        private Windows.Foundation.Size fullModeCameraResolution;
        private Windows.Foundation.Size splitModeCameraResolution;

        private async Task initializeCamera()
        {
            if (_photoCaptureDevice != null || _isInitializingCamera)
                return;
            _isInitializingCamera = true;

            try
            {
                //  capturingSemaphore = true;

                var captureResolutions = PhotoCaptureDevice.GetAvailableCaptureResolutions(sensor);

                var previewResolution = PhotoCaptureDevice.GetAvailablePreviewResolutions(sensor).Last();
                _photoCaptureDevice = await PhotoCaptureDevice.OpenAsync(sensor, previewResolution);

                double ratio = 1.0 * previewResolution.Width / previewResolution.Height;

                // Get the maximum resolution of the same preview ratio
                for (int i = 0; i < captureResolutions.Count; i++)
                {
                    Windows.Foundation.Size resolution = captureResolutions[i];

                    Debug.WriteLine("FullModeResolution = " + resolution);
                    if ((resolution.Width / resolution.Height) == ratio)
                    {
                        Debug.WriteLine("FullModeResolution Choosed = " + resolution);
                        fullModeCameraResolution = resolution;
                        break;
                    }
                }

                // Get the minimum resolution of the same preview ratio
                for (int i = captureResolutions.Count - 1; i >= 0; i--)
                {
                    Windows.Foundation.Size resolution = captureResolutions[i];

                    Debug.WriteLine("SplitModeResolution Choosed = " + resolution);
                    if ((resolution.Width / resolution.Height) == ratio)
                    {
                        Debug.WriteLine("SplitModeResolution Choosed = " + resolution);
                        splitModeCameraResolution = resolution;
                        break;
                    }
                }

                cameraPhotoFullMode = new CVImage((int)fullModeCameraResolution.Width, (int)fullModeCameraResolution.Height);
                croppedPhotoFullMode = new CVImage(cameraPhotoFullMode.Width, (int)(cameraPhotoFullMode.Width * 0.5625f));

                foreach (var r in captureResolutions)
                {
                    Debug.WriteLine(DEBUG_TAG + "initialize - avalaible capture resolutions: " + r);
                }

                foreach (var r in PhotoCaptureDevice.GetAvailablePreviewResolutions(sensor))
                {
                    Debug.WriteLine(DEBUG_TAG + "initialize - avalaible preview resolutions: " + r);
                }

                if (sensor == CameraSensorLocation.Back)
                {
                    CameraSettingsModel cameraSettings = (Application.Current as App).CameraSettings;
                    _photoCaptureDevice.SetProperty(KnownCameraPhotoProperties.FlashMode, cameraSettings.FlashState);
                }

                await _photoCaptureDevice.SetCaptureResolutionAsync(fullModeCameraResolution);
                photoSequence = _photoCaptureDevice.CreateCaptureSequence(1);

                await _photoCaptureDevice.SetPreviewResolutionAsync(previewResolution);

                //BackgroundVideoBrush.SetSource(_photoCaptureDevice);

                //HandleOrientationChanged();

                MainController.Singleton.SetupCamera(_photoCaptureDevice);
            }
            catch
            {
                uninitializeCamera();
            }

            _isInitializingCamera = false;
            GC.Collect();
        }

        private void uninitializeCamera()
        {
            if (_photoCaptureDevice != null)
            {
                MainController.Singleton.SetupCamera(null);
                _photoCaptureDevice.Dispose();
                _photoCaptureDevice = null;
            }
            if (photoSequence != null)
            {
                photoSequence = null;
            }
        }

        private async Task takePhoto()
        {
            Debug.WriteLine(DEBUG_TAG + "take photo");

            if (photoSequence == null)
            {
                return;
            }

            using (RandomStream myPhotoStream = new RandomStream(new MemoryStream()))
            {
                myPhotoStream.Seek(0);
                photoSequence.Frames[0].CaptureStream = myPhotoStream;

                ViewState state = getViewState();

                await _photoCaptureDevice.PrepareCaptureSequenceAsync(photoSequence);
                await photoSequence.StartCaptureAsync();

                myPhotoStream.Seek(0);

                CVImage finalPhoto = null;

                StreamImage LastPartnerImage = MainController.Singleton.LastPartnerImage;

                if (state != ViewState.SplitView || LastPartnerImage.imageData == null)
                {
                    cameraPhotoFullMode.SetSource(myPhotoStream.InternStream);

                    PixelUtil.Crop(cameraPhotoFullMode.Width, cameraPhotoFullMode.Height, cameraPhotoFullMode.Pixels,
                      croppedPhotoFullMode.Width, croppedPhotoFullMode.Height, 0, (cameraPhotoFullMode.Height - croppedPhotoFullMode.Height) / 2, croppedPhotoFullMode.Pixels);

                    finalPhoto = croppedPhotoFullMode;
                }
                else
                {
                    cameraPhotoFullMode.SetSource(myPhotoStream.InternStream);

                    PixelUtil.Resize(cameraPhotoFullMode.Width, cameraPhotoFullMode.Height, cameraPhotoFullMode.Pixels,
                        cameraPhotoSplitMode.Width, cameraPhotoSplitMode.Height, cameraPhotoSplitMode.Pixels);

                    partnerPhotoSplitMode.LoadJpeg(LastPartnerImage.imageData);

                    cameraPhotoSplitMode.ConcatHalfImages(partnerPhotoSplitMode, finalPhotoSplitMode);

                    finalPhoto = finalPhotoSplitMode;
                }

                Debug.WriteLine(DEBUG_TAG + "takePhoto - current view state: " + state);

                finalPhoto.SavePhoto().SyncPhoto();

                finalPhoto = null;

                GC.Collect();
            }

            Debug.WriteLine(DEBUG_TAG + "photo ok");
        }

    }
}
